package com.gmcloud.common.utils;

import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;

import java.beans.PropertyDescriptor;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Supplier;

/**
 * @author zl.sir
 * @version 1.0
 * @since 2023-03-16 11:47
 * @description bean工具类扩展
 */
public class BeanUtil extends org.springframework.beans.BeanUtils {

    private BeanUtil() {

    }

    /**
     * <p>对象数据的拷贝</p>
     *
     * @param sources 源
     * @param target  目标参数类型(Object::new)
     * @param <S>     源类型
     * @param <T>     目标类型
     * @return copy完成数据
     */
    public static <S, T> T copyProperties(S sources, Supplier<T> target) {
        T t = target.get();
        copyProperties(sources, t);
        return t;
    }

    /**
     * <p>带回调参数的对象数据的拷贝</p>
     *
     * @param sources  源
     * @param target   目标
     * @param callBack 回调参数
     * @param <S>      源类型
     * @param <T>      目标类型
     * @return 结果
     */
    public static <S, T> T copyProperties(S sources, Supplier<T> target, BeanUtilCallBack<S, T> callBack) {
        T t = target.get();
        copyProperties(sources, t);
        if (callBack != null) {
            callBack.callBack(sources, t);
        }
        return t;
    }

    /**
     * 拷贝不为空的字段
     * 使用场景：只想把 sources中有值的拷贝到target中，而不把target对应sources中没有值的赋值为null
     * 使用 BeanUtils.copyProperties();会全量拷贝，包括null
     *
     * @param sources 数据源
     * @param target  目标
     * @param <S>     数据源泛型
     * @param <T>     目标泛型
     */
    public static <S, T> void copyNotEmptyProperties(S sources, T target) {
        copyProperties(sources, target, getNullPropertyNames(sources));
    }

    /**
     * 拷贝不为空的字段:带回调参数
     * 使用场景：只想把 sources中有值的拷贝到target中，而不把target对应sources中没有值的赋值为null
     * 使用 BeanUtils.copyProperties();会全量拷贝，包括null
     *
     * @param sources 数据源
     * @param target  目标
     * @param <S>     数据源泛型
     * @param <T>     目标泛型
     * @return 目标数据
     */
    public static <S, T> T copyNotEmptyProperties(S sources, T target, BeanUtilCallBack<S, T> callBack) {
        copyProperties(sources, target, getNullPropertyNames(sources));
        if (callBack != null) {
            callBack.callBack(sources, target);
        }
        return target;
    }


    /**
     * <p>集合数据的拷贝</p>
     *
     * @param sources 数据源
     * @param target  目标参数类型(eg: UserVo:new)
     */
    public static <S, T> List<T> copyListProperties(List<S> sources, Supplier<T> target) {
        return copyListProperties(sources, target, null);
    }


    /**
     * <p>带回调参数的集合数据的拷贝</p>
     *
     * @param sources  数据源类
     * @param target   目标类::new (eg: UserVO::new)
     * @param callBack 回调函数 eg: BeanCopyUtil.copyListProperties(userLiset,UserVO:: new, (userDo,userVo) ->{
     *                 userVo.setSex("1");
     *                 userVO.setName(userDo.getUserName());
     *                 })
     * @return List<T> List<Bean>
     */
    public static <S, T> List<T> copyListProperties(List<S> sources, Supplier<T> target, BeanUtilCallBack<S, T> callBack) {
        return sources.stream().map(source -> {
            T t = target.get();
            copyProperties(source, t);
            if (callBack != null) {
                // 回调
                callBack.callBack(source, t);
            }
            return t;
        }).toList();
    }


    /**
     * 获取为空的字段
     */
    private static String[] getNullPropertyNames(Object source) {
        BeanWrapper src = new BeanWrapperImpl(source);
        PropertyDescriptor[] pds = src.getPropertyDescriptors();
        Set<String> emptyNames = new HashSet<>();
        for (PropertyDescriptor pd : pds) {
            Object srcValue = src.getPropertyValue(pd.getName());
            if (srcValue == null) {
                emptyNames.add(pd.getName());
            }
        }
        return emptyNames.toArray(new String[0]);
    }


    public interface BeanUtilCallBack<S, T> {
        void callBack(S s, T t);
    }

}
